import pickle
from ast import root
import engine


class RootVisitor(object):
    def __init__(self):
        self.globals = {}
    def visit_comment(self, obj):
        pass
    def visit_eval(self, obj):
        if obj.func.nodetype == "comment":
            return
        ev = ExprVisitor(self.globals, ())
        f = obj.func.accept(ev)
        print f.run()
    def visit_def(self, obj):
        ev = ExprVisitor(self.globals, obj.args)
        self.globals[obj.name] = obj.func.accept(ev)


class ExprVisitor(object):
    def __init__(self, globals, args):
        self.globals = globals
        self.args = list(args)
        self.indexed_args = dict((name, i) for i, name in enumerate(args))
    
    def resolve(self, name):
        if name in self.indexed_args:
            return engine.Arg(self.indexed_args[name])
        elif name in self.globals:
            return engine.Quoted(self.globals[name])
        elif name in engine.primitives:
            return engine.Quoted(engine.primitives[name])
        else:
            return engine.Quoted(engine.Lazy(self.globals, name))
    
    def visit_comment(self, obj):
        pass
    def visit_num(self, obj):
        return engine.Quoted(obj.value)
    def visit_str(self, obj):
        return engine.Quoted(obj.value)
    def visit_list(self, obj):
        return engine.Quoted([v.accept(self).func for v in obj.value])
    def visit_sym(self, obj):
        return self.resolve(obj.name)
    
    def visit_call(self, obj):
        func = obj.func.accept(self)
        args = [a.accept(self) for a in obj.args]
        return engine.Call(func, args)
    def visit_lambda(self, obj):
        ev = ExprVisitor(self.globals, obj.args + self.args)
        func = obj.func.accept(ev)
        boundargs = [engine.Arg(i) for i in range(len(self.args))]
        return engine.Closure(func, boundargs)


#class LazyResolvingVisitor(object):
#    def visit_Call(self, obj):
#        func = obj.func.accept(self)
#        args = [a.accept(self) for a in obj.args]
#        return engine.Call(func, args)
#    def visit_Closure(self, obj):
#        func = obj.func.accept(self)
#        args = [a.accept(self) for a in obj.boundargs]
#        return engine.Closure(func, args)
#    def visit_Quoted(self, obj):
#        if hasattr(obj.func, "accept"):
#            return engine.Quoted(obj.func.accept(self))
#        else:
#            return obj
#    def visit_Arg(self, obj):
#        return obj
#    def visit_Lazy(self, obj):
#        return obj.globals[obj.name]
#    def visit_Primitive(self, obj):
#        return obj
#
#def compile(text):
#    ast = root.parse(text)
#    rv = RootVisitor()
#    
#    # generate code for functions, ignore evaluation
#    for stmt in ast:
#        if stmt.nodetype == "def":
#            stmt.accept(rv)
#    
#    # resolve lazy-bound functions
#    lrv = LazyResolvingVisitor()
#    resolved = {}
#    for name, func in rv.globals.iteritems():
#        resolved[name] = func.accept(lrv)
#    
#    # generate code for "main function"
#    ev = ExprVisitor(resolved, ())
#    main = []
#    for stmt in ast:
#        if stmt.nodetype != "eval":
#            continue
#        if stmt.func.nodetype == "comment":
#            continue
#        main.append(stmt.func.accept(ev))
#    
#    return main
#
#def compilefile(f):
#    if hasattr(f, "read"):
#        text = f.read()
#    else:
#        text = open(f).read()
#    return compile(text)

def run(text):
    ast = root.parse(text)
    rv = RootVisitor()
    for stmt in ast:
        stmt.accept(rv)
    return rv

def runfile(f):
    if hasattr(f, "read"):
        text = f.read()
    else:
        text = open(f).read()
    return run(text)


runfile("test1.txt")




